package rtpengine

import (
	"common/log/newlog"
	"common/rtpengine/media"
	"common/rtpengine/rtp"
	"common/util"
	"errors"
	"sync"
	"time"
)

const (
	_trackLeftName  = "LeftLeg"
	_trackRightName = "RightLeg"

	DirectionLeft  = 1
	DirectionRight = 2
	DirectionBoth  = 3

	audioFrameLength = 20 * time.Millisecond
	videoFrameLength = 40 * time.Millisecond

	videoTimeInterval = uint32(3600)
)

var timeIntervalMappings = map[string]uint32{"amr": 160, "amr-octet": 160, "amr-wb": 320, "amr-wb-octet": 320}

type RTPSenderCallback func()

// for send
type SenderBinding struct {
	// 	id          string
	RightAddr string

	PayloadType PayloadType
	Payloader   rtp.Payloader

	// optional.
	Options SenderBindingOptions
}

type SenderBindingOptions struct {
	Ts       TS
	Ssrc     SSRC
	Sequence uint16
	Mtu      uint16
	Samples  uint32
}

type SenderBindingOption func(*SenderBindingOptions)

func SequenceType(sequence uint16) SenderBindingOption {
	return func(o *SenderBindingOptions) {
		o.Sequence = sequence
	}
}

func TSType(ts TS) SenderBindingOption {
	return func(o *SenderBindingOptions) {
		o.Ts = ts
	}
}

func SSRCType(ssrc SSRC) SenderBindingOption {
	return func(o *SenderBindingOptions) {
		o.Ssrc = ssrc
	}
}

func SamplesType(samples uint32) SenderBindingOption {
	return func(o *SenderBindingOptions) {
		o.Samples = samples
	}
}

func NewSenderBinding(rightAddr string, _payloadType PayloadType, loader rtp.Payloader, opts ...SenderBindingOption) *SenderBinding {
	remoteBinding := new(SenderBinding)
	remoteBinding.RightAddr = rightAddr
	remoteBinding.PayloadType = _payloadType
	remoteBinding.Payloader = loader
	for _, o := range opts {
		o(&remoteBinding.Options)
	}

	return remoteBinding
}

type RTPSender struct {
	tracks []*TrackLocal // support many tracks.

	// some control
	callId     string
	bIsPlaying bool
	//playMutex  sync.Mutex
	done sync.WaitGroup
	exit chan struct{}
	// play control
}

func NewRTPSender(callId string) *RTPSender {
	sender := new(RTPSender)
	//sender.leftBinding = _leftBinding
	//sender.rightBinding = _rightBinding
	////sender.localIP = _localIP
	//sender.localUdp = _localUDP
	//sender.localPort = _localPort
	sender.bIsPlaying = false
	sender.callId = callId
	return sender
}

// track不能重复.
func (r *RTPSender) AddTrack(track *TrackLocal) {
	for _, t := range r.tracks {
		if t.id == track.id {
			return
		}
	}
	r.tracks = append(r.tracks, track)
}

// track不能重复.
func (r *RTPSender) GetTrack(id string) *TrackLocal {
	for _, t := range r.tracks {
		if t.id == id {
			return t
		}
	}
	return nil
}

// Remove track
func (r *RTPSender) RemoveTrack(id string) {
	var _tracksD []*TrackLocal
	for index, t := range r.tracks {
		if t.id == id {
			if index < len(r.tracks)-1 {
				_tracksD = append(_tracksD, r.tracks[index+1:]...)
			}
			break
		}
		_tracksD = append(_tracksD, t)
	}
	r.tracks = _tracksD
}

// 音频发送..
func (r *RTPSender) AddLeftTrack(_localUDP string, binding SenderBinding) *TrackLocal {
	track := NewTrackLocal(_trackLeftName, _localUDP)
	track.Bind(binding)
	r.tracks = append(r.tracks, track)
	return track
}

func (r *RTPSender) AddRightTrack(_localUDP string, binding SenderBinding) *TrackLocal {
	track := NewTrackLocal(_trackRightName, _localUDP)
	track.Bind(binding)
	r.tracks = append(r.tracks, track)
	return track
}

// start all sessions use default buffer.
func (r *RTPSender) StartAllTracks() error {
	var writeErrs []error
	for _, t := range r.tracks {
		if err := t.StartSession(Config{}); err != nil {
			writeErrs = append(writeErrs, err)
		}
	}
	r.exit = make(chan struct{})
	r.bIsPlaying = true
	return util.FlattenErrs(writeErrs)
}

// 单次放音
func (r *RTPSender) SendAudio(media string, direction int, payload [][]byte, callback RTPSenderCallback) {
	r.SendAudioCycle(media, direction, false, nil, payload, nil, nil, callback)
}

// 循环放音
func (r *RTPSender) SendAudioCycle(media string, direction int, isCycle bool, sidBytes [][]byte, payload [][]byte, controlAudio *int32, controlVideo *int32, callback RTPSenderCallback) {
	log.InfoK(r.callId, "Start Sending audio")
	switch direction {
	case 0:
		fallthrough
	case DirectionLeft:
		r.sendSideAudioCycle(timeIntervalMappings[media], r.GetTrack(_trackLeftName), isCycle, sidBytes, payload, controlAudio, controlVideo, callback)
	case DirectionRight:
		r.sendSideAudioCycle(timeIntervalMappings[media], r.GetTrack(_trackRightName), isCycle, sidBytes, payload, controlAudio, controlVideo, callback)
	case DirectionBoth:
		_ = r.sendBothAudioCycle(timeIntervalMappings[media], isCycle, sidBytes, payload, controlAudio, controlVideo, callback)
	default:
		log.WarnK(r.callId, "Send audio failed, not supported direction %v", direction)
	}
	log.InfoK(r.callId, "Send audio done")
}

// 左右腿分别放音.
// 短点的腿加静音帧补偿到长点相同长度.
func (r *RTPSender) SendSolarAudio(audio string, leftPayload [][]byte, rightPayload [][]byte, sids [][]byte, callback RTPSenderCallback) {
	log.InfoK(r.callId, "Start Sending solar audio")
	leftTrack := r.GetTrack(_trackLeftName)
	rightTrack := r.GetTrack(_trackRightName)
	if leftTrack == nil || rightTrack == nil {
		log.Error("leftTrack || rightTrack = nil")
		return
	}

	ticker := time.NewTicker(audioFrameLength)
	defer ticker.Stop()

	sidCounter := 0
	sidPlay := false
	leftBaseLine := true
	lenBigger := len(leftPayload)
	if lenBigger < len(rightPayload) {
		lenBigger = len(rightPayload)
		leftBaseLine = false
	}
	leftTrack.SetSamples(timeIntervalMappings[audio])
	rightTrack.SetSamples(timeIntervalMappings[audio])
	for index := 0; index < lenBigger; index++ {
		if !sidPlay {
			if index >= len(leftPayload) || index >= len(rightPayload) {
				sidPlay = true
			}
		} else {
			sidCounter++
			if sidCounter >= len(sids) {
				sidCounter = 0
			}
		}

		select {
		case <-ticker.C:
			//leftDataPacket := s.leftSession.NewDataPacket(leftTs)
			//rightDataPacket := s.rightSession.NewDataPacket(rightTs)
			if sidPlay {
				if leftBaseLine {
					//leftDataPacket.SetPayload(leftPayload[index])
					//rightDataPacket.SetPayload(sids[sidCounter])
					_ = leftTrack.WriteSample(media.Sample{Data: leftPayload[index], PrevDroppedPackets: 0})
					_ = rightTrack.WriteSample(media.Sample{Data: sids[sidCounter], PrevDroppedPackets: 0})
				} else {
					//leftDataPacket.SetPayload(sids[sidCounter])
					//rightDataPacket.SetPayload(rightPayload[index])
					_ = leftTrack.WriteSample(media.Sample{Data: sids[sidCounter], PrevDroppedPackets: 0})
					_ = rightTrack.WriteSample(media.Sample{Data: rightPayload[index], PrevDroppedPackets: 0})
				}
			} else {
				//leftDataPacket.SetPayload(leftPayload[index])
				//rightDataPacket.SetPayload(rightPayload[index])
				_ = leftTrack.WriteSample(media.Sample{Data: leftPayload[index], PrevDroppedPackets: 0})
				_ = rightTrack.WriteSample(media.Sample{Data: rightPayload[index], PrevDroppedPackets: 0})
			}
			//if config.AppConf.Logger.Rtpdebug {
			log.DebugK(r.callId, "Send left audio: remoteAddr: %v, Ssrc: %v, Ts: %v, seq: %v", leftTrack.binding.RightAddr, leftTrack.binding.Options.Ssrc, leftTrack.binding.Options.Ts, leftTrack.binding.Options.Sequence)
			log.DebugK(r.callId, "Send right audio: remoteAddr: %v, Ssrc: %v, Ts: %v, seq: %v", rightTrack.binding.RightAddr, rightTrack.binding.Options.Ssrc, rightTrack.binding.Options.Ts, rightTrack.binding.Options.Sequence)
			//}

		case <-r.exit:
			log.InfoK(r.callId, "Send solar audio done")
			return
		}
	}
	if callback != nil {
		callback()
	}
	log.InfoK(r.callId, "Send solar audio done")
}

// 单腿放音
func (r *RTPSender) sendSideAudioCycle(timeInterval uint32, track *TrackLocal, isCycle bool, sidBytes [][]byte, payload [][]byte, controlAudio *int32,
	controlVideo *int32, callback RTPSenderCallback) {
	if track == nil {
		log.Errorf("sendSideAudioCycle: track:%s is nil", track.id)
		return
	}
	//Ts := binding.Ts
	track.SetSamples(timeInterval)
	ticker := time.NewTicker(audioFrameLength)
	defer ticker.Stop()
	r.done.Add(1)
	defer r.done.Done()

	payload = append(sidBytes, payload...)
	for index, length := 0, len(payload); index < length; {
		select {
		case <-ticker.C:
			if index == 0 && controlAudio != nil && controlVideo != nil && *controlAudio != *controlVideo {
				continue
			}
			//dataPacket := session.NewDataPacket(Ts)
			//dataPacket.SetPayload(payload[index])
			//_, _ = session.WriteData(dataPacket)
			_ = track.WriteSample(media.Sample{Data: payload[index], PrevDroppedPackets: 0})

			//if config.AppConf.Logger.Rtpdebug {
			log.DebugK(r.callId, "Send audio cycle, remoteAddr: %v, Ssrc: %v", track.binding.RightAddr, track.binding.Options.Ssrc)
			//}
		case <-r.exit:
			return
		}
		if isCycle && index == length-1 {
			index = len(sidBytes) - 1
		}
		index++
	}
	if callback != nil {
		callback()
	}
}

// 左右腿同时放音
func (r *RTPSender) sendBothAudioCycle(timeInterval uint32, isCycle bool, sidBytes [][]byte, payload [][]byte, controlAudio *int32, controlVideo *int32, callback RTPSenderCallback) error {
	leftTrack := r.GetTrack(_trackLeftName)
	rightTrack := r.GetTrack(_trackRightName)
	if leftTrack == nil || rightTrack == nil {
		return errors.New("leftTrack || rightTrack = nil")
	}
	leftTrack.SetSamples(timeInterval)
	rightTrack.SetSamples(timeInterval)
	ticker := time.NewTicker(audioFrameLength)
	defer ticker.Stop()

	payload = append(sidBytes, payload...)
	for index, length := 0, len(payload); index < length; {
		select {
		case <-ticker.C:
			if index == 0 && controlAudio != nil && controlVideo != nil && *controlAudio != *controlVideo {
				continue
			}
			_ = leftTrack.WriteSample(media.Sample{Data: payload[index], PrevDroppedPackets: 0})
			_ = rightTrack.WriteSample(media.Sample{Data: payload[index], PrevDroppedPackets: 0})

			//if config.AppConf.Logger.Rtpdebug {
			log.DebugK(r.callId, "Send left audio: remoteAddr: %v, Ssrc: %v, Ts: %v, seq: %v", leftTrack.binding.RightAddr, leftTrack.binding.Options.Ssrc, leftTrack.binding.Options.Ts, leftTrack.binding.Options.Sequence)
			log.DebugK(r.callId, "Send right audio: remoteAddr: %v, Ssrc: %v, Ts: %v, seq: %v", rightTrack.binding.RightAddr, rightTrack.binding.Options.Ssrc, rightTrack.binding.Options.Ts, rightTrack.binding.Options.Sequence)
			//}
		case <-r.exit:
			return nil
		}
		if isCycle && index == length-1 {
			index = len(sidBytes) - 1
		}
		index++
	}
	if callback != nil {
		callback()
	}
	return nil
}

// TODO: 视频和DTMF放音.
